﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace frag_boardgame
{
    /// <summary>
    /// A class for managing undo and redo.
    /// The class holds an Undo and a Redo stack of snapshots of the game's state,
    /// as well as a snapshot of the current game state.
    /// </summary>
    public class UndoManager
    {

        private Stack<GameData> undoStack;
        private Stack<GameData> redoStack;
        private GameData currentState;

        /// <summary>
        /// Creates an empty UndoManager.
        /// The object is invalid until Reset() is called.
        /// <remarks>Prior to any other operations, Reset() must be called to initialize the object.</remarks>
        /// </summary>
        public UndoManager()
        {
            undoStack = new Stack<GameData>();
            redoStack = new Stack<GameData>();
        }


        /// <summary>
        /// Push a copy of the given data into the undo stack, and clears the redo stack.
        /// </summary>
        /// <exception cref="InvalidOperationException">Manager was not initialized.</exception>
        /// <param name="data">The data to save.</param>
        public void AddSnapshot(GameData data)
        {
            if (!Valid())
            {
                Reset(data);
            }
            else
            {
                undoStack.Push(currentState);
                currentState = data.Clone();
                redoStack.Clear();
            }
        }

        /// <summary>
        /// Return a copy of the previous state, and add the current state to the redo stack.
        /// </summary>
        /// <exception cref="InvalidOperationException">Manager was not initialized, or Undo stack is empty.</exception>
        /// <returns>The state held by the manager prior to the last AddSnapshot or Redo method call.</returns>
        public GameData Undo()
        {
            if (!CanUndo())
            {
                throw new InvalidOperationException("UndoManager not initialized or undo list is empty");
            }
            redoStack.Push(currentState);
            currentState = undoStack.Pop();
            return currentState.Clone();
        }


        /// <summary>
        /// Returns the previously undone task, and moves it back to the undo stack.
        /// </summary>
        /// <exception cref="InvalidOperationException">Manager was not initialized, or Redo stack is empty.</exception>
        /// <returns>The state held by the manager prior to the last Undo call.</returns>
        public GameData Redo()
        {
            if (!CanRedo())
            {
                throw new InvalidOperationException("UndoManager not initialized or redo list is empty");
            }
            undoStack.Push(currentState);
            currentState = redoStack.Pop();
            return currentState.Clone();
        }

        /// <summary>
        /// Clears the undo and redo stacks, and resets the current state.
        /// </summary>
        /// <remarks>This method must be called once. If not, any other operation will fail.</remarks>
        /// <param name="initialState">The initial state to hold in the manager. 
        /// If this argument is null, the manager will be uninitialized.</param>
        public void Reset(GameData initialState)
        {
            undoStack.Clear();
            redoStack.Clear();

            currentState = initialState.Clone();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns>True if the object was initialized and the Undo stack is not empty.</returns>
        public bool CanUndo()
        {
            return Valid() && undoStack.Count > 0;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns>True if the object was initialized and the Redo stack is not empty.</returns>
        public bool CanRedo()
        {
            return Valid() && redoStack.Count > 0;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns>True if the object was initialized.</returns>
        public bool Valid()
        {
            return currentState != null;
        }
    }
}
